﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Web;
using BoxSharp;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;

namespace BoxSharpVideoSearch
{
    public class BoxSharpVideoSearch : AddonInstance
    {
        private AddonInterface myInterface;
        private ToolTip tip;
        private Dictionary<string, SearchEngine> engines = new Dictionary<string, SearchEngine>();
        private Window resultWindow;
        private StackPanel panelResult;
        private Button buttonShowResults;

        public override void AddonMain(AddonInterface addonInterface)
        {
            myInterface = addonInterface;

            myInterface.AddonInitializing += new AddonEventHandler(myInterface_AddonInitializing);
            myInterface.AddonFinalizing += new AddonEventHandler(myInterface_AddonFinalizing);
        }

        void myInterface_AddonFinalizing()
        {
            resultWindow.Close();
        }

        void myInterface_AddonInitializing()
        {
            string[] files = Directory.GetFiles(myInterface.Path, "*.txt");

            foreach (string file in files)
            {
                StreamReader reader = new StreamReader(file);
                string line = reader.ReadLine();

                if (line == "BoxSharp Search Engine")
                {
                    string name = reader.ReadLine();
                    string preUrl = reader.ReadLine();
                    string postUrl = reader.ReadLine();
                    string urlencode = reader.ReadLine();
                    string pageencode = reader.ReadLine();
                    string itemRegex = reader.ReadLine();

                    SearchEngine engine = new SearchEngine(name, preUrl, postUrl, urlencode, pageencode, itemRegex);
                    engines.Add(name, engine);
                }

                reader.Close();
            }

            tip = new System.Windows.Controls.ToolTip();
            tip.HasDropShadow = true;
            tip.PlacementTarget = myInterface.Box;
            tip.Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom;
            tip.IsOpen = false;
            tip.Padding = new System.Windows.Thickness(5);
            tip.VerticalOffset = 5;

            resultWindow = new Window();
            resultWindow.Background = System.Windows.Media.Brushes.AliceBlue;
            resultWindow.ResizeMode = ResizeMode.NoResize;
            resultWindow.WindowStyle = WindowStyle.None;
            resultWindow.Left = myInterface.MainWindow.Left;
            resultWindow.Top = myInterface.MainWindow.Top + myInterface.MainWindow.ActualHeight + 5;
            resultWindow.SizeToContent = SizeToContent.WidthAndHeight;
            resultWindow.ShowInTaskbar = false;
            resultWindow.Topmost = true;

            ScrollViewer resultView = new ScrollViewer();
            resultView.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
            resultView.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
            resultView.MaxHeight = 600;

            panelResult = new StackPanel();
            resultWindow.Content = resultView;

            resultView.Content = panelResult;

            buttonShowResults = new Button();
            buttonShowResults.Click += new RoutedEventHandler(buttonShowResults_Click);

            myInterface.Box.TextChanged += new TextChangedEventHandler(Box_TextChanged);
            myInterface.OnCommand += new CommandEventHandler(myInterface_OnCommand);
        }

        void myInterface_OnCommand(string bscommand)
        {
            if (bscommand.ToLower().StartsWith("/searchv "))
            {
                List<VideoInfo> results = new List<VideoInfo>();

                foreach (string engine in engines.Keys)
                {
                    try
                    {
                        results.AddRange(engines[engine].Search(bscommand.Substring(9)));
                    }
                    catch (System.Exception)
                    {
                    }
                }

                if (results.Count == 0)
                {
                    MessageBox.Show("No video found.", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
                    return;
                }

                List<VideoInfo> sortResults = new List<VideoInfo>();

                foreach (VideoInfo result in results)
                {
                    if (sortResults.Count == 0)
                    {
                        sortResults.Add(result);
                    }
                    else
                    {
                        int i = 0;
                        for (i = 0; i < sortResults.Count; ++i)
                        {
                            if (sortResults[i].PlayRank < result.PlayRank)
                            {
                                break;
                            }
                        }

                        sortResults.Insert(i, result);
                    }
                }

                panelResult.Children.Clear();

                for (int i = 0; i < sortResults.Count; ++i)
                {
                    VideoInfo result = sortResults[i];

                    if (i > 0)
                    {
                        Separator sep = new Separator();
                        sep.Foreground = System.Windows.Media.Brushes.LightGray;
                        sep.Background = System.Windows.Media.Brushes.LightGray;
                        sep.Margin = new Thickness(5, 0, 5, 0);
                        panelResult.Children.Add(sep);
                    }

                    StackPanel panelItem = new StackPanel();
                    panelItem.Orientation = Orientation.Horizontal;
                    panelItem.MouseEnter += new System.Windows.Input.MouseEventHandler(panelItem_MouseEnter);
                    panelItem.MouseLeave += new System.Windows.Input.MouseEventHandler(panelItem_MouseLeave);
                    panelItem.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(panelItem_MouseLeftButtonUp);
                    panelItem.MouseRightButtonUp += new System.Windows.Input.MouseButtonEventHandler(panelItem_MouseRightButtonUp);
                    panelItem.ToolTip = "Left Click to view in browser, Right Click to close the result window.";
                    panelItem.SetValue(ToolTipService.ShowDurationProperty, 3000);
                    panelItem.SetValue(ToolTipService.InitialShowDelayProperty, 2000);
                    panelItem.SetValue(ToolTipService.BetweenShowDelayProperty, 3000);
                    panelItem.Tag = result.Link;
                    panelItem.Cursor = System.Windows.Input.Cursors.Hand;
                    panelItem.Background = System.Windows.Media.Brushes.AliceBlue;

                    Image itemImage = new Image();
                    itemImage.Stretch = System.Windows.Media.Stretch.UniformToFill;
                    itemImage.Source = new System.Windows.Media.Imaging.BitmapImage(new Uri(result.Image));

                    Label itemImageLabel = new Label();
                    itemImageLabel.Padding = new Thickness(10);
                    itemImageLabel.Content = itemImage;
                    itemImageLabel.VerticalAlignment = VerticalAlignment.Top;
                    itemImageLabel.HorizontalAlignment = HorizontalAlignment.Left;
                    itemImageLabel.Width = 150;

                    panelItem.Children.Add(itemImageLabel);

                    StackPanel panelDescription = new StackPanel();
                    panelDescription.Margin = new Thickness(10);

                    TextBlock textTitle = new TextBlock();
                    textTitle.FontSize = 13;
                    textTitle.Margin = new Thickness(0, 5, 0, 5);
                    textTitle.HorizontalAlignment = HorizontalAlignment.Left;
                    if (result.Title.Length > 50)
                    {
                        textTitle.Text = result.Title.Substring(0, 50) + "...";
                    }
                    else
                    {
                        textTitle.Text = result.Title;
                    }

                    TextBlock textDescription = new TextBlock();
                    if (result.Description.Length > 100)
                    {
                        textDescription.Text = result.Description.Substring(0, 100) + "...";
                    }
                    else
                    {
                        textDescription.Text = result.Description;
                    }
                    textDescription.MaxWidth = 350;
                    textDescription.TextWrapping = TextWrapping.Wrap;
                    textDescription.Margin = new Thickness(0, 5, 0, 5);
                    textDescription.HorizontalAlignment = HorizontalAlignment.Left;
                    textDescription.Foreground = System.Windows.Media.Brushes.Gray;

                    TextBlock textViews = new TextBlock();
                    textViews.Text = "Views:" + result.PlayRank.ToString();
                    textViews.HorizontalAlignment = HorizontalAlignment.Left;
                    textViews.Foreground = System.Windows.Media.Brushes.Gray;

                    TextBlock textDuration = new TextBlock();
                    textDuration.Text = "[" + result.Provider + "] " + result.Duration.ToString();
                    textDuration.HorizontalAlignment = HorizontalAlignment.Left;
                    textDuration.Foreground = System.Windows.Media.Brushes.Gray;

                    panelDescription.Children.Add(textTitle);
                    panelDescription.Children.Add(textDuration);
                    panelDescription.Children.Add(textDescription);
                    panelDescription.Children.Add(textViews);

                    panelItem.Children.Add(panelDescription);
                    panelResult.Children.Add(panelItem);
                }

                resultWindow.Show();

                if (myInterface.Panel1.Children.Contains(buttonShowResults))
                {
                    myInterface.Panel1.Children.Remove(buttonShowResults);
                }
                buttonShowResults.Content = "Video:" + bscommand.Substring(9);

                myInterface.Box.Text = "";
            }
            else
            {
                foreach (string engine in engines.Keys)
                {
                    if (bscommand.ToLower().StartsWith("/" + engine.ToLower() + " "))
                    {
                        string query = bscommand.Substring(2 + engine.Length);

                        engines[engine].Query(query);

                        myInterface.Box.Text = "";
                        break;
                    }
                }
            }
        }

        void buttonShowResults_Click(object sender, RoutedEventArgs e)
        {
            resultWindow.Show();
            if (myInterface.Panel1.Children.Contains(buttonShowResults))
            {
                myInterface.Panel1.Children.Remove(buttonShowResults);
            }
        }

        void panelItem_MouseRightButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            resultWindow.Hide();
            if (!myInterface.Panel1.Children.Contains(buttonShowResults))
            {
                myInterface.Panel1.Children.Add(buttonShowResults);
            }
        }

        void panelItem_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            StackPanel panelItem = sender as StackPanel;

            string url = panelItem.Tag as string;

            try
            {
                System.Diagnostics.Process.Start(url);

                resultWindow.Hide();
                if (!myInterface.Panel1.Children.Contains(buttonShowResults))
                {
                    myInterface.Panel1.Children.Add(buttonShowResults);
                }
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("Failed to open the URL. Here's the error message:\r\n" + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        void panelItem_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
        {
            StackPanel panelItem = sender as StackPanel;
            panelItem.Background = System.Windows.Media.Brushes.Transparent;
        }

        void panelItem_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            StackPanel panelItem = sender as StackPanel;
            panelItem.Background = System.Windows.Media.Brushes.White;
        }

        void Box_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (myInterface.Box.Text.ToLower() == ("/searchv") || myInterface.Box.Text.ToLower().StartsWith("/searchv "))
            {
                tip.Content = "/searchv query\r\nSearch the specified video in all video search engines.";
                tip.IsOpen = true;
            }
            else
            {
                bool found = false;

                foreach (string engine in engines.Keys)
                {
                    if (myInterface.Box.Text.ToLower() == ("/" + engine.ToLower()) || myInterface.Box.Text.ToLower().StartsWith("/" + engine.ToLower() + " "))
                    {
                        tip.Content = "/" + engine.ToLower() + " query\r\nSearch the specified video in " + engine + ".";
                        tip.IsOpen = true;
                        found = true;
                        break;
                    }
                }

                if (!found)
                {
                    tip.IsOpen = false;
                }
            }
        }
    }

    public class VideoInfo
    {
        public string Title;
        public string Description;
        public string Link;
        public string Image;
        public int PlayRank;
        public string Provider;
        public TimeSpan Duration;
    }

    public class SearchEngine
    {
        private string name;
        private string preURL;
        private string postURL;
        private Encoding urlencoding;
        private Encoding pageencoding;
        private Regex chkItem;

        public SearchEngine(string engineName, string preUrl, string postUrl, string urlencode, string pageencode, string itemRegex)
        {
            name = engineName;
            preURL = preUrl;
            postURL = postUrl;
            urlencoding = Encoding.GetEncoding(urlencode);
            pageencoding = Encoding.GetEncoding(pageencode);
            chkItem = new Regex(itemRegex, RegexOptions.IgnoreCase);
        }

        public void Query(string word)
        {
            try
            {
                System.Diagnostics.Process.Start(preURL + System.Web.HttpUtility.UrlEncode(word, urlencoding) + postURL);
            }
            catch (System.Exception ex)
            {
                System.Windows.MessageBox.Show("Failed to send the query. Here's the error message:\r\n" + ex.Message, "Error", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
            }
        }

        public string GetQueryURL(string word)
        {
            return preURL + System.Web.HttpUtility.UrlEncode(word, urlencoding) + postURL;
        }

        public List<VideoInfo> Search(string word)
        {
            string queryUrl = preURL + System.Web.HttpUtility.UrlEncode(word, urlencoding) + postURL;

            WebRequest req = HttpWebRequest.Create(queryUrl);
            WebResponse rep = req.GetResponse();
            StreamReader reader = new StreamReader(rep.GetResponseStream(), pageencoding);

            string content = reader.ReadToEnd();

            reader.Close();
            reader.Dispose();
            rep.Close();

            string baseUrl = queryUrl.Substring(0, queryUrl.LastIndexOf('/'));

            MatchCollection matches = chkItem.Matches(content);

            List<VideoInfo> results = new List<VideoInfo>();
            foreach (Match m in matches)
            {
                string title = replaceSymbols(m.Groups["title"].Value);
                string description = replaceSymbols(dealWebText(m.Groups["description"].Value));
                string link = m.Groups["link"].Value;

                if (!link.StartsWith("http://"))
                {
                    if (link.StartsWith("/"))
                    {
                        link = baseUrl + link;
                    }
                    else
                    {
                        link = baseUrl + "/" + link;
                    }
                }

                string image = m.Groups["image"].Value;
                string playRank = m.Groups["rank"].Value.Replace(",", "").Replace(" ", "");

                string durStr = m.Groups["duration"].Value.Replace(" ","");

                if (durStr.Length <= 5)
                {
                    durStr = "00:" + durStr;
                }

                TimeSpan duration = TimeSpan.Parse(durStr);

                VideoInfo newItem = new VideoInfo();
                newItem.Title = title;
                newItem.Description = description;
                newItem.Image = image;
                newItem.Link = link;
                newItem.PlayRank = Convert.ToInt32(playRank);
                newItem.Provider = name;
                newItem.Duration = duration;

                results.Add(newItem);
            }

            return results;
        }

        private string replaceSymbols(string text)
        {
            return text.Replace("&quot;", "\"").Replace("&#39;", "'").Replace("&amp;", "&").Replace("&middot;", ".");
        }

        private string dealWebText(string text)
        {
            StringBuilder sb = new StringBuilder();

            int state = 0;

            text = text.Replace("\r", "").Replace("\n", "").Replace("\t", "");

            for (int i = 0; i < text.Length; ++i)
            {
                switch (state)
                {
                    case 0:
                        {
                            if (text[i] == '<')
                            {
                                state = 1;
                            }
                            else
                            {
                                sb.Append(text[i]);
                            }
                        }
                        break;
                    case 1:
                        {
                            if (text[i] == '>')
                            {
                                state = 0;
                            }
                        }
                        break;
                }
            }

            text = sb.ToString();

            sb = new StringBuilder();

            state = 0;
            for (int i = 0; i < text.Length; ++i)
            {
                switch (state)
                {
                    case 0:
                        {
                            if (text[i] == ' ')
                            {
                                state = 1;
                            }
                            sb.Append(text[i]);
                        }
                        break;
                    case 1:
                        {
                            if (text[i] != ' ')
                            {
                                sb.Append(text[i]);
                                state = 0;
                            }
                        }
                        break;
                }
            }

            text = sb.ToString();

            return text;
        }

        public string Name
        {
            get
            {
                return name;
            }
        }
    }
}
